Skip to content

chore: charges add custom currency support#4755

Merged
turip merged 3 commits into
mainfrom
chore/add-custom-currency-support
Jul 21, 2026
Merged

chore: charges add custom currency support#4755
turip merged 3 commits into
mainfrom
chore/add-custom-currency-support

Conversation

@turip

@turip turip commented Jul 20, 2026

Copy link
Copy Markdown
Member

Overview

This patch uses the shared custom currency/fiat union types in charges. Right now all create calls return errors if custom currency is being used.

On the ledger side the same guard is in place, for easier identification of the unsupported operands.

Notes for reviewer

Greptile Summary

This PR adds custom-currency support to billing charges. The main changes are:

  • Stores either a fiat code or custom-currency reference on each charge.
  • Resolves currencies before creating credit-purchase charges.
  • Maps and filters charges using the new currency representation.
  • Updates Ent schemas, generated models, migrations, and search views.

Confidence Score: 4/5

Credit-purchase creation, currency-filtered activity queries, and database upgrades can fail on the updated paths.

  • The resolved currency never reaches the new persistence input.
  • Funded activity filtering requires mutually exclusive currency fields at once.
  • Charge mapping can fail when the new edge was not eagerly loaded.
  • Custom-currency grant flows are accepted and then rejected by lineage.
  • The migration leaves the search view stale and cannot roll back custom rows.

Credit-purchase adapters and lineage handling, shared charge mapping, and both custom-currency migration files.

Important Files Changed

Filename Overview
openmeter/billing/charges/creditpurchase/adapter/charge.go Adds custom-currency queries, but the create path does not pass the resolved currency into persistence.
openmeter/billing/charges/creditpurchase/adapter/funded_credit_activity.go Maps custom currencies but combines fiat and custom filter alternatives with AND.
openmeter/billing/charges/models/chargemeta/model.go Introduces shared currency persistence and mapping that depends on the custom-currency edge being loaded.
openmeter/billing/charges/lineage/service.go Carries resolved currency objects into lineage while explicitly rejecting custom currencies.
openmeter/ent/schema/charges.go Adds mutually exclusive fiat and custom currency references and expands the search-view schema.
tools/migrate/migrations/20260720093016_charges-custom-currencies.up.sql Updates charge tables without recreating the persisted search view.
tools/migrate/migrations/20260720093016_charges-custom-currencies.down.sql Restores a non-null fiat column without handling rows that use custom currencies.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Credit purchase request] --> B[Resolve currency by namespace and code]
  B --> C[Normalize amount precision]
  C --> D[Persist charge]
  D --> E{Currency type}
  E -->|Fiat| F[Store currency code]
  E -->|Custom| G[Store custom currency ID]
  F --> H[Map charge with resolved currency]
  G --> H
  H --> I[Funded activity and lineage flows]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
  A[Credit purchase request] --> B[Resolve currency by namespace and code]
  B --> C[Normalize amount precision]
  C --> D[Persist charge]
  D --> E{Currency type}
  E -->|Fiat| F[Store currency code]
  E -->|Custom| G[Store custom currency ID]
  F --> H[Map charge with resolved currency]
  G --> H
  H --> I[Funded activity and lineage flows]
Loading

Comments Outside Diff (1)

  1. openmeter/billing/charges/creditpurchase/adapter/charge.go, line 84-89 (link)

    P1 Resolved Currency Is Dropped

    service.Create resolves the requested currency, but only uses it for amount normalization before calling this adapter. This new chargemeta.CreateInput requires Currency, yet the value is not carried through creditpurchase.CreateInput or set here, so every valid credit-purchase create returns currency is required before persistence.

    Context Used: AGENTS.md (source)

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: openmeter/billing/charges/creditpurchase/adapter/charge.go
    Line: 84-89
    
    Comment:
    **Resolved Currency Is Dropped**
    
    `service.Create` resolves the requested currency, but only uses it for amount normalization before calling this adapter. This new `chargemeta.CreateInput` requires `Currency`, yet the value is not carried through `creditpurchase.CreateInput` or set here, so every valid credit-purchase create returns `currency is required` before persistence.
    
    **Context Used:** AGENTS.md ([source](https://app.greptile.com/openmeter/github/openmeterio/openmeter/-/custom-context?memory=eb020e3b-8e3b-45ea-a8b7-4006b2b2065b))
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Claude Code Fix in Codex

Fix All in Claude Code Fix All in Codex

Prompt To Fix All With AI
Fix the following 6 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 6
openmeter/billing/charges/creditpurchase/adapter/charge.go:84-89
**Resolved Currency Is Dropped**

`service.Create` resolves the requested currency, but only uses it for amount normalization before calling this adapter. This new `chargemeta.CreateInput` requires `Currency`, yet the value is not carried through `creditpurchase.CreateInput` or set here, so every valid credit-purchase create returns `currency is required` before persistence.

### Issue 2 of 6
openmeter/billing/charges/creditpurchase/adapter/funded_credit_activity.go:67-74
**Mutually Exclusive Filters Are ANDed**

`HasCreditPurchaseWith` combines these predicates with AND, but the new database constraint requires a charge to have either `fiat_currency_code` or `custom_currency_id`, never both. Supplying any currency filter therefore excludes the matching funded activities instead of returning them.

```suggestion
		query = query.Where(dbchargecreditpurchasecreditgrant.HasCreditPurchaseWith(
			dbchargecreditpurchase.Or(
				dbchargecreditpurchase.HasCustomCurrencyWith(
					dbcustomcurrency.CodeEQ(*input.Currency),
					dbcustomcurrency.Namespace(input.Customer.Namespace),
					dbcustomcurrency.DeletedAtIsNil(),
				),
				dbchargecreditpurchase.FiatCurrencyCodeEQ(*input.Currency),
			),
		))
```

### Issue 3 of 6
openmeter/billing/charges/models/chargemeta/model.go:255-263
**Mapping Requires an Eager-Loaded Edge**

Every charge mapping now calls `CustomCurrencyOrErr`, including fiat charges and entities returned by update or mark-voided operations. Those write queries do not load the new edge, so the accessor returns an edge-not-loaded error and otherwise successful operations fail while mapping their result. The mapper must not require an eager-loaded edge on every path, or every caller must load it first.

### Issue 4 of 6
openmeter/billing/charges/lineage/service.go:114-119
**Custom Credit Lifecycle Is Rejected**

The create service now accepts and resolves a custom currency, but credit-purchase grant flows call advance-lineage backfill with that resolved currency. This new guard rejects the operation, so a custom-currency promotional or external credit purchase cannot complete its lifecycle. Either reject this currency earlier as unsupported or add lineage support before accepting the charge.

### Issue 5 of 6
tools/migrate/migrations/20260720093016_charges-custom-currencies.up.sql:1-6
**Search View Keeps Its Old Shape**

The generated `ChargesSearchV1` model now selects `custom_currency_id`, but this migration never recreates the existing `charges_search_v1s` view with that column. After upgrading an existing database, charge-search queries request a column the installed view does not expose and fail at runtime. Recreate the view in this migration after altering its source tables.

### Issue 6 of 6
tools/migrate/migrations/20260720093016_charges-custom-currencies.down.sql:1-6
**Rollback Cannot Convert Custom Charges**

A custom-currency charge has `currency IS NULL` and only `custom_currency_id` populated. This rollback drops that ID and then restores `currency` to NOT NULL, so it fails as soon as any custom-currency charge exists. The rollback needs an explicit conversion or deletion policy before restoring the old constraint.

Reviews (1): Last reviewed commit: "chore: charges add custom currency suppo..." | Re-trigger Greptile

Greptile also left 5 inline comments on this PR.

Context used:

  • Context used - CLAUDE.md (source)
  • Context used - AGENTS.md (source)

Summary by CodeRabbit

  • New Features
    • Added customer charge creation for flat-fee and usage-based charges.
    • Added unified currency handling across charges, credits, invoicing, and ledger operations.
    • Added support for resolving and displaying fiat or custom currency information.
  • Bug Fixes
    • Improved currency validation, precision rounding, and currency matching.
    • Added clear errors when unsupported custom currencies are used.
    • Improved charge and credit-purchase mapping reliability.

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR introduces currencies.Currency for fiat/custom currency handling, adds custom-currency persistence, propagates currency-aware charge and ledger contracts, wires CurrencyResolver, and refactors API customer-charge creation to return a single charge.

Changes

Custom currency support

Layer / File(s) Summary
Currency model and persistence
openmeter/currencies/..., openmeter/ent/schema/..., tools/migrate/...
Adds fiat/custom currency modeling, database fields and constraints, custom-currency edges, and updated search views.
Charge and service flows
openmeter/billing/charges/..., openmeter/ledger/...
Propagates currencies.Currency, uses currency codes at ledger boundaries, and rejects unsupported custom-currency operations.
API and dependency wiring
api/v3/..., app/common/..., cmd/.../wire_gen.go
Adds CreateCustomerCharge, wires CurrencyResolver, and updates service configuration contracts.
Supporting tests and helpers
**/*_test.go, test/credits/base.go
Constructs fiat test currencies through the shared currency test utility and updates currency comparisons.
Rating and subscription sync
openmeter/billing/rating/..., openmeter/billing/worker/subscriptionsync/...
Adds currency-calculator access through the standard line interface and stores currencies.Currency in subscription target state.

Estimated code review effort: 5 (Critical) | ~150 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant CreateCustomerChargesHandler
  participant ChargesService
  participant CurrencyResolver
  Client->>CreateCustomerChargesHandler: submit flat-fee or usage-based charge
  CreateCustomerChargesHandler->>ChargesService: CreateCustomerCharge(input)
  ChargesService->>CurrencyResolver: resolve currency code
  CurrencyResolver-->>ChargesService: return currencies.Currency
  ChargesService-->>CreateCustomerChargesHandler: return Charge
  CreateCustomerChargesHandler-->>Client: return API charge
Loading

Possibly related PRs

Suggested labels: kind/refactor

Suggested reviewers: tothandras

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.84% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding custom currency support to charges.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/add-custom-currency-support

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread openmeter/billing/charges/models/chargemeta/model.go Outdated
Comment thread openmeter/billing/charges/lineage/service.go
Comment thread tools/migrate/migrations/20260720093016_charges-custom-currencies.up.sql Outdated
Comment thread tools/migrate/migrations/20260720093016_charges-custom-currencies.down.sql Outdated
@turip
turip force-pushed the chore/add-custom-currency-support branch from 523a84d to 7854f8e Compare July 21, 2026 10:49
@greptile-apps

greptile-apps Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Too many files changed for review. (163 files found, 100 file limit)

Bypass the limit by tagging @greptile-apps to review.

@turip turip added release-note/feature Release note: Exciting New Features area/billing labels Jul 21, 2026
@turip
turip marked this pull request as ready for review July 21, 2026 11:24
@turip
turip requested a review from a team as a code owner July 21, 2026 11:24
Comment thread openmeter/billing/charges/meta/intent.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (5)
api/v3/handlers/customers/charges/convert_test.go (1)

1-16: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding tests for the new CreateCustomerChargeInput builders.

This file only swaps the currency test fixture; I don't see tests exercising fromAPICreateChargeFlatFeeRequest/fromAPICreateChargeUsageBasedRequest in convert.go, which changed their return type entirely (from ChargeIntent to CreateCustomerChargeInput with nested FlatFee/UsageBased payloads). Worth confirming coverage exists somewhere, or adding cases here.

As per path instructions for **/*_test.go: "Make sure the tests are comprehensive and cover the changes."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/v3/handlers/customers/charges/convert_test.go` around lines 1 - 16, Add
focused tests in this charges conversion test file for
fromAPICreateChargeFlatFeeRequest and fromAPICreateChargeUsageBasedRequest,
asserting each returns the correct CreateCustomerChargeInput variant and
preserves its nested FlatFee or UsageBased payload fields, including the
selected currency fixture. Cover representative valid inputs and the changed
return structure.

Source: Path instructions

openmeter/billing/charges/creditpurchase/service/create.go (1)

24-26: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider using .IsCustom() for better encapsulation.

Since the new currencies.Currency abstraction provides an IsCustom() method, consider using it here for cleaner encapsulation instead of comparing the underlying type against currencyx.CurrencyTypeCustom.

♻️ Proposed refactor
-		if input.Intent.Currency.Type() == currencyx.CurrencyTypeCustom {
+		if input.Intent.Currency.IsCustom() {
			return creditpurchase.ChargeWithGatheringLine{}, fmt.Errorf("custom currency %s is not supported for credit purchases: %w", input.Intent.Currency.GetCode(), meta.ErrCustomCurrencyNotSupported)
		}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openmeter/billing/charges/creditpurchase/service/create.go` around lines 24 -
26, Update the currency validation in the credit purchase creation flow to use
input.Intent.Currency.IsCustom() instead of comparing Currency.Type() with
currencyx.CurrencyTypeCustom. Keep the existing error message and return
behavior unchanged.
openmeter/billing/charges/creditpurchase/adapter/funded_credit_activity_test.go (1)

281-282: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer s.T().Context() over context.Background().

As per the coding guidelines, you should use the test context instead of context.Background() to ensure proper context propagation and timely test cancellation.

♻️ Proposed fix
 func (s *ListFundedCreditActivitiesSuite) TestFiltersByCurrency() {
-	ctx := context.Background()
+	ctx := s.T().Context()
 	ns := "test-funded-activity-currency"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@openmeter/billing/charges/creditpurchase/adapter/funded_credit_activity_test.go`
around lines 281 - 282, Update TestFiltersByCurrency in
ListFundedCreditActivitiesSuite to initialize ctx from s.T().Context() instead
of context.Background(), preserving the existing test behavior while propagating
test cancellation.

Source: Coding guidelines

openmeter/billing/charges/service/pendinglines.go (1)

115-121: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer using currencies.NewFiatCurrency for instantiation.

Instead of manually building the fiat currency and initializing the currencies.Currency struct, you can use the NewFiatCurrency constructor. This provides better encapsulation and stays consistent with its usage in invoice.go.

♻️ Proposed refactor
-	currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat).
-		WithCode(input.Currency).
-		Build()
+	resolvedCurrency, err := currencies.NewFiatCurrency(input.Currency)
	if err != nil {
		return nil, fmt.Errorf("resolving fiat currency %q: %w", input.Currency, err)
	}
-	resolvedCurrency := currencies.Currency{Currency: currency}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openmeter/billing/charges/service/pendinglines.go` around lines 115 - 121,
Replace the manual fiat currency builder and direct currencies.Currency
initialization in the pending-line flow with currencies.NewFiatCurrency,
preserving input.Currency and the existing error propagation behavior; follow
the constructor usage established in invoice.go.
tools/migrate/migrations/20260720093016_charges-custom-currencies.down.sql (1)

4-8: 🩺 Stability & Availability | 🔵 Trivial

Rollback heads-up: SET NOT NULL will fail once custom-currency charges exist.

Just so it's on your radar: reverting currency back to NOT NULL errors out if any charge row was created with custom_currency_id set (i.e. currency NULL), and dropping custom_currency_id discards that data. That's inherent to reverting the feature, not a hand-edit ask — but worth knowing before relying on this down-migration in an environment where custom currencies have already been used.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/migrate/migrations/20260720093016_charges-custom-currencies.down.sql`
around lines 4 - 8, Update the down migration for charge_usage_based,
charge_flat_fees, and charge_credit_purchases to handle rows with
custom_currency_id before dropping that column and restoring currency to NOT
NULL. Preserve or explicitly remove those custom-currency rows according to the
project’s rollback policy, ensuring the migration does not fail on existing NULL
currency values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@openmeter/billing/charges/charge.go`:
- Around line 190-212: Update Charge.GetCurrency to match the ChargeAccessor
interface by returning only currencies.Currency. Remove error returns and handle
nil or invalid charge cases using the established zero-value currency behavior,
while preserving the existing currency lookup for valid flat-fee,
credit-purchase, and usage-based charges.

In `@openmeter/currencies/currency.go`:
- Around line 60-66: Update Currency.Validate in
openmeter/currencies/currency.go (lines 60-66) to collect validation failures,
preserve field context through wrapped errors, and return
models.NewNillableGenericValidationError(errors.Join(errs...)); update the
Validate method in openmeter/currencies/adapter/currencies.go (lines 30-40) to
collect both validation conditions and return them through the same helper.

---

Nitpick comments:
In `@api/v3/handlers/customers/charges/convert_test.go`:
- Around line 1-16: Add focused tests in this charges conversion test file for
fromAPICreateChargeFlatFeeRequest and fromAPICreateChargeUsageBasedRequest,
asserting each returns the correct CreateCustomerChargeInput variant and
preserves its nested FlatFee or UsageBased payload fields, including the
selected currency fixture. Cover representative valid inputs and the changed
return structure.

In
`@openmeter/billing/charges/creditpurchase/adapter/funded_credit_activity_test.go`:
- Around line 281-282: Update TestFiltersByCurrency in
ListFundedCreditActivitiesSuite to initialize ctx from s.T().Context() instead
of context.Background(), preserving the existing test behavior while propagating
test cancellation.

In `@openmeter/billing/charges/creditpurchase/service/create.go`:
- Around line 24-26: Update the currency validation in the credit purchase
creation flow to use input.Intent.Currency.IsCustom() instead of comparing
Currency.Type() with currencyx.CurrencyTypeCustom. Keep the existing error
message and return behavior unchanged.

In `@openmeter/billing/charges/service/pendinglines.go`:
- Around line 115-121: Replace the manual fiat currency builder and direct
currencies.Currency initialization in the pending-line flow with
currencies.NewFiatCurrency, preserving input.Currency and the existing error
propagation behavior; follow the constructor usage established in invoice.go.

In `@tools/migrate/migrations/20260720093016_charges-custom-currencies.down.sql`:
- Around line 4-8: Update the down migration for charge_usage_based,
charge_flat_fees, and charge_credit_purchases to handle rows with
custom_currency_id before dropping that column and restoring currency to NOT
NULL. Preserve or explicitly remove those custom-currency rows according to the
project’s rollback policy, ensuring the migration does not fail on existing NULL
currency values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0c9e2ffc-fdf8-4853-a0dd-2f9b15f0f6f0

📥 Commits

Reviewing files that changed from the base of the PR and between 1c6fe7d and 3e4f5a7.

⛔ Files ignored due to path filters (33)
  • openmeter/ent/db/chargecreditpurchase.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargecreditpurchase/chargecreditpurchase.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargecreditpurchase/where.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargecreditpurchase_create.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargecreditpurchase_query.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargecreditpurchase_update.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargeflatfee.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargeflatfee/chargeflatfee.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargeflatfee/where.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargeflatfee_create.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargeflatfee_query.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargeflatfee_update.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargessearchv1.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargessearchv1/chargessearchv1.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargessearchv1/where.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargeusagebased.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargeusagebased/chargeusagebased.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargeusagebased/where.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargeusagebased_create.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargeusagebased_query.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargeusagebased_update.go is excluded by !**/ent/db/**
  • openmeter/ent/db/client.go is excluded by !**/ent/db/**
  • openmeter/ent/db/customcurrency.go is excluded by !**/ent/db/**
  • openmeter/ent/db/customcurrency/customcurrency.go is excluded by !**/ent/db/**
  • openmeter/ent/db/customcurrency/where.go is excluded by !**/ent/db/**
  • openmeter/ent/db/customcurrency_create.go is excluded by !**/ent/db/**
  • openmeter/ent/db/customcurrency_query.go is excluded by !**/ent/db/**
  • openmeter/ent/db/customcurrency_update.go is excluded by !**/ent/db/**
  • openmeter/ent/db/entmixinaccessor.go is excluded by !**/ent/db/**
  • openmeter/ent/db/migrate/schema.go is excluded by !**/ent/db/**
  • openmeter/ent/db/mutation.go is excluded by !**/ent/db/**
  • openmeter/ent/db/runtime.go is excluded by !**/ent/db/**
  • tools/migrate/migrations/atlas.sum is excluded by !**/*.sum, !**/*.sum
📒 Files selected for processing (126)
  • api/v3/handlers/customers/charges/convert.go
  • api/v3/handlers/customers/charges/convert_test.go
  • api/v3/handlers/customers/charges/create.go
  • api/v3/handlers/customers/charges/handler.go
  • api/v3/handlers/customers/credits/convert.go
  • api/v3/handlers/customers/credits/convert_test.go
  • api/v3/server/server.go
  • app/common/billing.go
  • app/common/charges.go
  • app/common/openmeter_billingworker.go
  • cmd/billing-worker/wire_gen.go
  • cmd/jobs/internal/wire.go
  • cmd/jobs/internal/wire_gen.go
  • cmd/server/wire_gen.go
  • openmeter/billing/charges/adapter/search_test.go
  • openmeter/billing/charges/api.go
  • openmeter/billing/charges/charge.go
  • openmeter/billing/charges/creditpurchase/adapter.go
  • openmeter/billing/charges/creditpurchase/adapter/charge.go
  • openmeter/billing/charges/creditpurchase/adapter/funded_credit_activity.go
  • openmeter/billing/charges/creditpurchase/adapter/funded_credit_activity_test.go
  • openmeter/billing/charges/creditpurchase/adapter/mapper.go
  • openmeter/billing/charges/creditpurchase/charge.go
  • openmeter/billing/charges/creditpurchase/charge_test.go
  • openmeter/billing/charges/creditpurchase/service/create.go
  • openmeter/billing/charges/creditpurchase/service/external_test.go
  • openmeter/billing/charges/creditpurchase/service/get.go
  • openmeter/billing/charges/creditpurchase/service/promotional_test.go
  • openmeter/billing/charges/creditpurchase/service/void.go
  • openmeter/billing/charges/flatfee/adapter/charge.go
  • openmeter/billing/charges/flatfee/adapter/detailedline_test.go
  • openmeter/billing/charges/flatfee/adapter/intentoverride.go
  • openmeter/billing/charges/flatfee/adapter/intentoverride_test.go
  • openmeter/billing/charges/flatfee/adapter/mapper.go
  • openmeter/billing/charges/flatfee/adapter/realizationrun_test.go
  • openmeter/billing/charges/flatfee/charge.go
  • openmeter/billing/charges/flatfee/charge_test.go
  • openmeter/billing/charges/flatfee/service/create.go
  • openmeter/billing/charges/flatfee/service/creditsonly.go
  • openmeter/billing/charges/flatfee/service/lineengine.go
  • openmeter/billing/charges/flatfee/service/manualedit.go
  • openmeter/billing/charges/flatfee/service/realizations/credittheninvoice.go
  • openmeter/billing/charges/flatfee/service/realizations/preview.go
  • openmeter/billing/charges/lineage/lineage_test.go
  • openmeter/billing/charges/lineage/service.go
  • openmeter/billing/charges/lineage/service/service.go
  • openmeter/billing/charges/meta/charge.go
  • openmeter/billing/charges/meta/errors.go
  • openmeter/billing/charges/meta/intent.go
  • openmeter/billing/charges/models/chargemeta/model.go
  • openmeter/billing/charges/service.go
  • openmeter/billing/charges/service/advance.go
  • openmeter/billing/charges/service/api.go
  • openmeter/billing/charges/service/base_test.go
  • openmeter/billing/charges/service/create.go
  • openmeter/billing/charges/service/creditpurchase_test.go
  • openmeter/billing/charges/service/invoicable_test.go
  • openmeter/billing/charges/service/invoice.go
  • openmeter/billing/charges/service/lineage_test.go
  • openmeter/billing/charges/service/pendinglines.go
  • openmeter/billing/charges/service/recognition.go
  • openmeter/billing/charges/service/service.go
  • openmeter/billing/charges/service/taxcode_test.go
  • openmeter/billing/charges/service/truncation_test.go
  • openmeter/billing/charges/testutils/service.go
  • openmeter/billing/charges/usagebased/adapter/charge.go
  • openmeter/billing/charges/usagebased/adapter/detailedline_test.go
  • openmeter/billing/charges/usagebased/adapter/intentoverride.go
  • openmeter/billing/charges/usagebased/adapter/intentoverride_test.go
  • openmeter/billing/charges/usagebased/adapter/mapper.go
  • openmeter/billing/charges/usagebased/charge.go
  • openmeter/billing/charges/usagebased/detailedline.go
  • openmeter/billing/charges/usagebased/rating.go
  • openmeter/billing/charges/usagebased/service/create.go
  • openmeter/billing/charges/usagebased/service/creditheninvoice_test.go
  • openmeter/billing/charges/usagebased/service/creditsonly_test.go
  • openmeter/billing/charges/usagebased/service/lineengine.go
  • openmeter/billing/charges/usagebased/service/linemapper.go
  • openmeter/billing/charges/usagebased/service/rating/service_test.go
  • openmeter/billing/charges/usagebased/service/rating/subtract/subtract_test.go
  • openmeter/billing/charges/usagebased/service/rating/testutils/testutils.go
  • openmeter/billing/charges/usagebased/service/run/payment_test.go
  • openmeter/billing/charges/usagebased/service/triggers.go
  • openmeter/billing/charges/usagebased/service/triggers_test.go
  • openmeter/billing/creditgrant/service/service.go
  • openmeter/billing/rating/line.go
  • openmeter/billing/rating/service/detailedline.go
  • openmeter/billing/stdinvoiceline.go
  • openmeter/billing/worker/subscriptionsync/service/base_test.go
  • openmeter/billing/worker/subscriptionsync/service/creditsonly_test.go
  • openmeter/billing/worker/subscriptionsync/service/reconciler/patchcharge_test.go
  • openmeter/billing/worker/subscriptionsync/service/reconciler/patchchargeflatfee.go
  • openmeter/billing/worker/subscriptionsync/service/reconciler/patchchargeusagebased.go
  • openmeter/billing/worker/subscriptionsync/service/sync_credittheninvoice_test.go
  • openmeter/billing/worker/subscriptionsync/service/targetstate/targetstate.go
  • openmeter/billing/worker/subscriptionsync/service/targetstate/targetstateitem.go
  • openmeter/currencies/adapter/currencies.go
  • openmeter/currencies/currency.go
  • openmeter/currencies/currency_test.go
  • openmeter/currencies/testutils/currency/currency.go
  • openmeter/ent/schema/charges.go
  • openmeter/ent/schema/chargescreditpurchase.go
  • openmeter/ent/schema/chargesflatfee.go
  • openmeter/ent/schema/chargesusagebased.go
  • openmeter/ent/schema/custom_currencies.go
  • openmeter/ledger/chargeadapter/creditpurchase.go
  • openmeter/ledger/chargeadapter/creditpurchase_test.go
  • openmeter/ledger/chargeadapter/flatfee.go
  • openmeter/ledger/chargeadapter/flatfee_test.go
  • openmeter/ledger/chargeadapter/usagebased.go
  • openmeter/ledger/chargeadapter/usagebased_test.go
  • openmeter/ledger/customerbalance/expired_loader_test.go
  • openmeter/ledger/customerbalance/service.go
  • openmeter/ledger/customerbalance/service_test.go
  • openmeter/ledger/customerbalance/testenv_test.go
  • openmeter/ledger/recognizer/recognize.go
  • openmeter/ledger/recognizer/service.go
  • openmeter/ledger/recognizer/service_test.go
  • openmeter/server/router/router.go
  • openmeter/server/server_test.go
  • pkg/framework/entutils/entedge/ornil.go
  • test/app/stripe/invoice_credits_test.go
  • test/credits/base.go
  • tools/migrate/migrations/20260720093016_charges-custom-currencies.down.sql
  • tools/migrate/migrations/20260720093016_charges-custom-currencies.up.sql
  • tools/migrate/views.sql
💤 Files with no reviewable changes (1)
  • openmeter/billing/charges/creditpurchase/service/get.go

Comment thread openmeter/billing/charges/charge.go
Comment thread openmeter/currencies/currency.go
Comment thread openmeter/billing/charges/creditpurchase/adapter/charge.go
Comment thread openmeter/billing/charges/creditpurchase/adapter/mapper.go Outdated
Comment thread openmeter/billing/charges/models/chargemeta/model.go Outdated
Comment thread openmeter/billing/charges/usagebased/adapter/mapper.go Outdated
Comment thread openmeter/billing/charges/usagebased/service/triggers.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
openmeter/billing/charges/models/chargemeta/model.go (1)

37-43: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Preserve field context in validation errors.

As per coding guidelines, please wrap the errors for Intent and IntentMutableFields to preserve their field context so it's clear where the validation failed.

♻️ Proposed fix
-	if err := i.Intent.Validate(); err != nil {
-		errs = append(errs, err)
-	}
-
-	if err := i.IntentMutableFields.Validate(); err != nil {
-		errs = append(errs, err)
-	}
+	if err := i.Intent.Validate(); err != nil {
+		errs = append(errs, fmt.Errorf("intent: %w", err))
+	}
+
+	if err := i.IntentMutableFields.Validate(); err != nil {
+		errs = append(errs, fmt.Errorf("intent mutable fields: %w", err))
+	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openmeter/billing/charges/models/chargemeta/model.go` around lines 37 - 43,
Update the validation error handling in the model’s validation method for Intent
and IntentMutableFields to wrap each returned error with its corresponding field
name before appending it to errs. Preserve the underlying validation error while
making the failing field explicit.

Source: Coding guidelines

🧹 Nitpick comments (1)
openmeter/billing/charges/usagebased/adapter/detailedline.go (1)

218-221: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the required FromDB... conversion naming.

This helper converts a database entity into a domain model, but fromDBDetailedLine does not follow the repository’s FromDB... naming convention. Rename it consistently and update the call site.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openmeter/billing/charges/usagebased/adapter/detailedline.go` around lines
218 - 221, Rename the database-to-domain conversion helper fromDBDetailedLine to
the repository-standard FromDBDetailedLine name, and update every call site to
use the renamed function.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@openmeter/billing/charges/models/chargemeta/model.go`:
- Around line 37-43: Update the validation error handling in the model’s
validation method for Intent and IntentMutableFields to wrap each returned error
with its corresponding field name before appending it to errs. Preserve the
underlying validation error while making the failing field explicit.

---

Nitpick comments:
In `@openmeter/billing/charges/usagebased/adapter/detailedline.go`:
- Around line 218-221: Rename the database-to-domain conversion helper
fromDBDetailedLine to the repository-standard FromDBDetailedLine name, and
update every call site to use the renamed function.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 622ad0eb-af8a-4dd5-8f16-e74a8bd0540b

📥 Commits

Reviewing files that changed from the base of the PR and between 3e4f5a7 and 723e57f.

📒 Files selected for processing (17)
  • openmeter/billing/charges/creditpurchase/adapter/charge.go
  • openmeter/billing/charges/creditpurchase/adapter/funded_credit_activity.go
  • openmeter/billing/charges/creditpurchase/adapter/mapper.go
  • openmeter/billing/charges/creditpurchase/adapter/predicate.go
  • openmeter/billing/charges/flatfee/adapter/charge.go
  • openmeter/billing/charges/flatfee/adapter/intentoverride.go
  • openmeter/billing/charges/flatfee/adapter/mapper.go
  • openmeter/billing/charges/flatfee/adapter/realizationrun.go
  • openmeter/billing/charges/meta/intent.go
  • openmeter/billing/charges/models/chargemeta/model.go
  • openmeter/billing/charges/usagebased/adapter/charge.go
  • openmeter/billing/charges/usagebased/adapter/detailedline.go
  • openmeter/billing/charges/usagebased/adapter/intentoverride.go
  • openmeter/billing/charges/usagebased/adapter/mapper.go
  • openmeter/billing/charges/usagebased/adapter/realizationrun.go
  • openmeter/currencies/adapter/currencies.go
  • openmeter/currencies/currency.go
🚧 Files skipped from review as they are similar to previous changes (7)
  • openmeter/billing/charges/meta/intent.go
  • openmeter/billing/charges/creditpurchase/adapter/funded_credit_activity.go
  • openmeter/currencies/currency.go
  • openmeter/billing/charges/flatfee/adapter/intentoverride.go
  • openmeter/billing/charges/usagebased/adapter/charge.go
  • openmeter/billing/charges/flatfee/adapter/charge.go
  • openmeter/billing/charges/creditpurchase/adapter/charge.go

@turip
turip merged commit ced75b6 into main Jul 21, 2026
26 checks passed
@turip
turip deleted the chore/add-custom-currency-support branch July 21, 2026 14:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/billing release-note/feature Release note: Exciting New Features

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants